package com.starlink.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.util.StrUtil;
import com.starlink.common.enums.ErrorCode;
import com.starlink.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.http.HttpHeaders;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author: zyh
 * @date: 2023-07-05
 */
@Slf4j
public class ZipFileUtils {

    private static ExecutorService executorService = ExecutorBuilder.create().setCorePoolSize(5).setMaxPoolSize(200).setWorkQueue(new LinkedBlockingDeque<>(100000)).build();

    /**
     * zip压缩包下载
     *
     * @param pictures 图片或者文件夹
     * @param excel    excel文件
     * @param fileName 文件名
     */
    public static void downloadZip(List<File> pictures, Workbook excel, String fileName, HttpServletResponse response) {
        try (ZipOutputStream zout = new ZipOutputStream(response.getOutputStream())) {
            String excelName = fileName;
            fileName = fileName + ".zip";
            response.setContentType("application/zip");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(fileName,
                    "UTF-8"));

            // 将excel写入ZipOutputStream
            if (excel != null) {
                zout.putNextEntry(new ZipEntry(excelName + ".xlsx"));
                excel.write(zout);
                zout.closeEntry();
            }

            // 将pictures写入ZipOutputStream
            for (File picture : pictures) {
                try (InputStream in = new FileInputStream(picture)) {
                    zout.putNextEntry(new ZipEntry(picture.getName()));
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = in.read(buffer)) > 0) {
                        zout.write(buffer, 0, length);
                    }
                    zout.closeEntry();
                }
            }
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.INTER_ERR, "Excel和图片文件压缩包下载失败");
        }
    }

    public static void downloadZip2(List<File> pictures, Workbook excel, String fileName,
                                    HttpServletResponse response) {
        try (ZipOutputStream zout = new ZipOutputStream(response.getOutputStream())) {
            String excelName = fileName;
            fileName = fileName + ".zip";
            response.setContentType("application/zip");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(fileName,
                    "UTF-8"));

            // 将excel写入ZipOutputStream
            if (excel != null) {
                zout.putNextEntry(new ZipEntry(excelName + ".xlsx"));
                excel.write(zout);
                zout.closeEntry();
            }

            // 将pictures写入ZipOutputStream中的图片文件夹
            for (File picture : pictures) {
                try (InputStream in = new FileInputStream(picture)) {
                    zout.putNextEntry(new ZipEntry(excelName + "/" + picture.getName()));
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = in.read(buffer)) > 0) {
                        zout.write(buffer, 0, length);
                    }
                    zout.closeEntry();
                }
            }
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.INTER_ERR, "Excel和图片文件压缩包下载失败");
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名
     * @param fileUrl  文件地址
     * @return
     * @throws Exception
     */
    public static File download(String fileName, String fileUrl) throws Exception {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        String path = System.getProperty("java.io.tmpdir");
        File file = new File(path + "/" + fileName); // 指定本地文件路径和文件名
        try (InputStream inputStream = connection.getInputStream();
             OutputStream outputStream = new FileOutputStream(file)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
        return file;
    }

    public static File download(String fileName, String dir, String fileUrl) throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        String path = System.getProperty("java.io.tmpdir");
        if (StringUtils.isNotEmpty(dir)) {
            path = path + File.separator + dir;
        }
        File directory = new File(path);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        File file = new File(path + File.separator + fileName);
        try (InputStream inputStream = connection.getInputStream();
             OutputStream outputStream = new FileOutputStream(file)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return file;
    }

    /**
     * 创建zip文件
     *
     * @param source   被打包的文件目录
     * @param fileName 打包后存放的目录及文件名
     * @throws FileNotFoundException
     */
    public static void createZipFile(Workbook excel, String source, String fileName, HttpServletResponse response) {
        File file = new File(System.getProperty("java.io.tmpdir") + File.separator + source);
        try {
            long startTime = System.currentTimeMillis();
            log.info("createZipFile filePath={}", file.getAbsolutePath());
            String excelName = fileName;
            fileName = fileName + ".zip";
            response.setContentType("application/zip");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(fileName,
                    "UTF-8"));
            ZipOutputStream out = new ZipOutputStream(response.getOutputStream());

            // 将excel写入ZipOutputStream
            if (excel != null) {
                out.putNextEntry(new ZipEntry(excelName + ".xlsx"));
                excel.write(out);
                out.closeEntry();
            }

            // 将图片写入ZipOutputStream
            StopWatch stopWatch = StopWatch.create("add file to zip");
            stopWatch.start();
            log.info("开始图片添加到压缩包...");
            putZipFile(out, file, "");
            stopWatch.stop();
            log.info("图片添加到压缩包结束 总耗时={}s", stopWatch.getTotalTimeSeconds());

            out.close();
            log.info("创建zip结束 总耗时={}ms", System.currentTimeMillis() - startTime);
        } catch (IOException e) {
            throw new RuntimeException("创建zip文件失败", e);
        }
    }

    public static String createAndUploadZipFile(Workbook excel, String source, String fileName) {
        // 文件夹
        File file = new File(System.getProperty("java.io.tmpdir") + File.separator + source);
        try {
            log.info("createZipFile filePath={}", file.getAbsolutePath());
            String excelName = fileName;
            fileName = fileName + ".zip";

            // 创建临时压缩文件
            File zipFile = File.createTempFile("temp", ".zip");
            FileOutputStream fos = new FileOutputStream(zipFile);
            ZipOutputStream out = new ZipOutputStream(fos);

            // 将excel写入ZipOutputStream
            if (excel != null) {
                out.putNextEntry(new ZipEntry(excelName + ".xlsx"));
                excel.write(out);
                out.closeEntry();
            }

            StopWatch stopWatch = StopWatch.create("add file to zip");
            stopWatch.start();
            log.info("开始图片添加到压缩包...");
            ZipUtils2.putZipFile(out, file, "");
            stopWatch.stop();
            log.info("图片添加到压缩包结束 总耗时={}s", stopWatch.getTotalTimeSeconds());
            out.close();

            FileInputStream inputStream = new FileInputStream(zipFile);
            MultipartFile multipartFile = StreamUtil.getMultipartFile(inputStream, fileName);

            String dateStr = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            String objectName = StrUtil.format("WeeklyReport/{}/{}", dateStr, fileName);
            long uploadStartTime = System.currentTimeMillis();
            String ossUrl = OSSUtil.instance.uploadMultipartFile3(multipartFile, objectName);
            log.info("上传oss 总耗时={}s", (System.currentTimeMillis() - uploadStartTime)/1000);
            return ossUrl;
        } catch (IOException e) {
            throw new RuntimeException("创建并上传zip文件失败", e);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String createAndUploadExcelFile(Workbook excel, String source, String fileName) {
        // 文件夹
        try {
            // 创建临时压缩文件
            File excelFile = File.createTempFile(fileName, ".xlsx");
            FileOutputStream out = new FileOutputStream(excelFile);
            excel.write(out);
            out.close();

            FileInputStream inputStream = new FileInputStream(excelFile);
            MultipartFile multipartFile = StreamUtil.getMultipartFile(inputStream, fileName);

            String dateStr = DateUtil.format(new Date(), "yyyyMMddHHmmss");
            String objectName = StrUtil.format("WeeklyReport/{}/{}", dateStr, fileName + ".xlsx");
            long uploadStartTime = System.currentTimeMillis();
            String ossUrl = OSSUtil.instance.uploadMultipartFile3(multipartFile, objectName);
            log.info("上传oss 总耗时={}s", (System.currentTimeMillis() - uploadStartTime)/1000);
            return ossUrl;
        } catch (IOException e) {
            throw new RuntimeException("创建并上传excel文件失败", e);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将对应文件夹下的文件及文件夹写入对应文件夹目录下
     *
     * @param out
     * @param file
     * @param pareFileName
     */
    private static void putZipFile(final ZipOutputStream out, File file, String pareFileName) {
        try {
            File files[] = file.listFiles();
            for (File dirFile : files) {
                if (dirFile.isDirectory()) {
                    String newParentName = pareFileName + dirFile.getName() + File.separator;
                    ZipEntry zipEntry = new ZipEntry(newParentName);
                    out.putNextEntry(zipEntry);
                    putZipFile(out, dirFile, newParentName);
                } else {
                    String newParentName = pareFileName + dirFile.getName();
                    FileInputStream fi = new FileInputStream(dirFile);
                    BufferedInputStream origin = new BufferedInputStream(fi);
                    ZipEntry entry = new ZipEntry(newParentName);
                    out.putNextEntry(entry);
                    int count;
                    while ((count = origin.read()) != -1) {
                        out.write(count);
                    }
                    origin.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("压缩文件异常", e);
        }
    }
}
